/**
 * Copyright (C) 2008 Christopher Allen Ogden
 *
 *     This file is part of Odysi.
 *
 *  Odysi is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  Odysi is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with Odysi.  If not, see <http://www.gnu.org/licenses/>.
 */

#pragma once

#include <SDL.h>
#include <SDL_opengl.h>
#include <vector>
#include <string>
#include "singleton.h"
#include "std.h"
#include "pixel.h"
#include "config.h"

/**
 * Handles all rendering.
 *
 * @author  Ogden, Chris
 */
class RenderSystem:public Singleton<RenderSystem>
{
    public:
        /**
         * Constructor.
         */
        RenderSystem():_lastTexture(0) { }

        /**
         * Initialize the rendering system.
         */
        void initSystem(void)
        {
            // Enable texture mapping.
            glEnable(GL_TEXTURE_2D);

            // Create the viewport.
            glViewport(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
            glMatrixMode(GL_PROJECTION);
            glLoadIdentity();
            glOrtho(0, SCREEN_WIDTH, SCREEN_HEIGHT, 0, -1, 1);
            glMatrixMode(GL_MODELVIEW);
            glLoadIdentity();

            // Enable blending.
            glEnable(GL_BLEND);
            glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);

            // Use flat shading for speed.
            glShadeModel(GL_FLAT);

            // Use the fastest methods.
            glHint(GL_POLYGON_SMOOTH_HINT, GL_FASTEST);
            glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST);
            glHint(GL_TEXTURE_COMPRESSION_HINT, GL_FASTEST);
        }

        /**
         * Renders a quad to the screen.
         *
         * @param texture   The ID of the texture source.
         * @param x1        The X coordinate to begin drawing from.
         * @param y1        The Y coordinate to begin drawing from.
         * @param x2        The X coordinate to end drawing to.
         * @param y2        The Y coordinate to end drawing to.
         * @param u1        The X coordinate of the texture to begin mapping from.
         * @param v1        The Y coordinate of the texture to begin mapping from.
         * @param u2        The X coordinate of the texture to end mapping to.
         * @param v2        The Y coordinate of the texture to end mapping to.
         */
        void quad(GLuint texture,
                  GLint x1,
                  GLint y1,
                  GLint x2,
                  GLint y2,
                  GLfloat u1,
                  GLfloat v1,
                  GLfloat u2,
                  GLfloat v2);

        /**
         * Renders a filled quad to the screen.
         *
         * @param x1    The first X coordinate.
         * @param y1    The first Y coordinate.
         * @param x2    The second X coordinate.
         * @param y2    The second Y coordinate.
         */
        void solidQuad(GLint x1, GLint y1, GLint x2, GLint y2);

        /**
         * Renders a line.
         *
         * @param x1    The first X coordinate.
         * @param y1    The first Y coordinate.
         * @param x2    The second X coordinate.
         * @param y2    The second Y coordinate.
         */
        void line(GLint x1, GLint y1, GLint x2, GLint y2);

        /**
         * Renders an empty quad.
         *
         * @param x1    The first X coordinate.
         * @param y1    The first Y coordinate.
         * @param x2    The second X coordinate.
         * @param y2    The second Y coordinate.
         */
        void emptyQuad(GLint x1, GLint y1, GLint x2, GLint y2);

        /**
         * Render a pre-loaded image.
         *
         * @param image The ID of the image to display.
         * @param x     The X location.
         * @param y     The Y location.
         */
        void image(unsigned int image, int x, int y);

        /**
         * Sets the current color.
         *
         * @param red   The red value.
         * @param green The green value.
         * @param blue  The blue value.
         * @param alpha The alpha value.
         */
        void color(GLfloat red = 1.0f, GLfloat green = 1.0f, GLfloat blue = 1.0f, GLfloat alpha = 1.0f);

        /**
         * Renders all objects to the screen.
         */
        void render(void);

        /**
         * Destructor.
         */
        ~RenderSystem();

        /**
         * Load an image from a TGA file.
         *
         * @param fileName  The name of the file to load.
         * @return          The image ID.
         */
        unsigned int load(const std::string &fileName);
    private:
        /**
         * Quad storage object.
         */
        struct Quad
        {
            GLuint texture;
            GLint vertex1[2];
            GLint vertex2[2];
            GLint vertex3[2];
            GLint vertex4[2];
            GLfloat uv1[2];
            GLfloat uv2[2];
            GLfloat uv3[2];
            GLfloat uv4[2];
        };

        /**
         * Solid quad storage object.
         */
        struct SolidQuad
        {
            GLint vertex1[2];
            GLint vertex2[2];
            GLint vertex3[2];
            GLint vertex4[2];
        };

        /**
         * Line storage object.
         */
        struct Line
        {
            GLint vertex1[2];
            GLint vertex2[2];
        };

        /**
         * Image storage object.
         */
        struct Image
        {
            GLuint texture;
            GLfloat v1;
            GLfloat v2;
            GLfloat u1;
            GLfloat u2;
            unsigned int width, height;
            std::string fileName;
        };

        /**
         * Command storage object.
         */
        struct Command
        {
            enum CommandType {QUAD, COLOR, SOLIDQUAD, LINE};
            CommandType command;
            void *data;
        };

        /**
         * Vector of Commands.
         */
        typedef std::vector<Command> CommandList;

        /**
         * Stores a list of commands.
         */
        CommandList _commands;

        /**
         * Stores the last texture used, to prevent
         * unneeded switching.
         */
        GLuint _lastTexture;

        /**
         * The pixel data.
         */
        typedef std::vector<Pixel> PixelData;

        /**
         * Image list.
         */
        typedef std::vector<Image> ImageList;
        ImageList _images;
};
